﻿' ProductDataSource.vb
' compile with: /doc:ProductDataSource.xml
' <copyright>Copyright (c) Microsoft Corporation. All rights reserved.
' </copyright>
' <summary>This file contains product data lookup methods.</summary>

Imports System
Imports System.Data
Imports System.Diagnostics



''' <summary>
''' This class provides access to the xml data.
''' </summary>
Public Class ProductDataSource
    Implements IDisposable

    ''' <summary>The InvalidProductId constant is used to provide a
    ''' consistent value to be assigned to a product ID to flag that the
    ''' value is not valid.</summary>
    Private Const invalidProductId As Integer = -1

    ''' <summary>The product ID xml element contains an integer that
    ''' uniquely identifies the product.</summary>
    Private Const productIdColumnName As String = "ProductID"

    ''' <summary>The wholesale price xml element contains the wholesale
    ''' price of the product.</summary>
    Private Const wholesalePriceColumnName As String = "WholesalePrice"

    ''' <summary>The retail price xml element contains the retail price
    ''' of the product.</summary>
    Private Const retailPriceColumnName As String = "RetailPrice"

    ''' <summary>A DataSet is used to store the product information data
    ''' while the application is running.</summary>
    Private productDataSet As System.Data.DataSet

    ''' <summary>This boolean is used to track if the object has been disposed yet.
    ''' </summary>
    Private disposed As Boolean



    ''' <summary>
    ''' The ProductDataSource class provides access to the product data xml file.
    ''' </summary>
    Public Sub New()

        ' No initialization is required for this class.
    End Sub

    ''' <summary>The GetRetailPrice method gets the retail price
    ''' for the specified product.</summary>
    ''' <param name="productId">The ID of the product to get the
    ''' retail price for.</param>
    ''' <returns>The retail price. If there is an error
    ''' the returned price is 0.</returns>
    Public Function GetRetailPrice(ByVal productId As Integer) As Decimal

        Return GetDecimalFieldValue(productId, retailPriceColumnName)
    End Function

    ''' <summary>The GetWholesalePrice method gets the wholesale price
    ''' for the specified product.</summary>
    ''' <param name="productId">The ID of the product to get the
    ''' wholesale price for.</param>
    ''' <returns>The wholesale price. If there is an error
    ''' the returned price is 0.</returns>
    Public Function GetWholesalePrice(ByVal productId As Integer) As Decimal

        Return GetDecimalFieldValue(productId, wholesalePriceColumnName)
    End Function

    ''' <summary>
    ''' Return all information for a product.</summary>
    ''' <param name="productId">The product identifier.</param>
    ''' <returns>DataSet object with one table and one row. 
    ''' If productId not found the table will contain zero rows.</returns>
    Public Function GetProductInfo(ByVal productId As Integer) As DataSet

        Dim dataSet As DataSet = Nothing


        Try

            If productDataSet Is Nothing Then
                Dim msgWin As ErrorWindow = New ErrorWindow("GetProductInfo", "ProductDataSetNotInitialized")
                msgWin.ShowDialog()
                Exit Try
            End If

            ' Copy the product data schema (but not the data) 
            ' to the data set that will be returned.
            dataSet = productDataSet.Clone()

            ' Look up the data row for the requested productId.
            Dim productRow As DataRow
            productRow = findProduct(productId)

            If productRow IsNot Nothing Then

                ' Copy the matching row from the product data to the
                ' data set that will be returned.
                Dim dataTable As DataTable
                dataTable = dataSet.Tables(0)
                dataTable.ImportRow(productRow)
            End If

        Catch ex As Exception

            Dim errWin As ErrorWindow = New ErrorWindow("GetProductInfo", ex)
            errWin.ShowDialog()

        End Try


        Return dataSet
    End Function

    ''' <summary>The ReadProductData method reads the product information
    ''' from the xml file into the product data storage. The product
    ''' data is stored in a DataSet while the application is running to
    ''' simplify access and improve performance.</summary>
    ''' <param name="productDataFilename">Name of the xml file containing
    ''' the product data.</param>
    ''' <remarks>On success, productDataSet member contains the xml.
    ''' Throws SecurityException or FileNotFoundException on error.</remarks>
    Public Sub ReadProductData(ByVal productDataFilename As String)

        Try

            If productDataSet Is Nothing Then
                productDataSet = New System.Data.DataSet
            Else
                productDataSet = Nothing
                productDataSet = New System.Data.DataSet
            End If


            productDataSet.Locale = System.Globalization.CultureInfo.CurrentCulture
            productDataSet.ReadXml((System.Windows.Forms.Application.StartupPath & "\") + productDataFilename, System.Data.XmlReadMode.ReadSchema)


        Catch ex As Exception

            Dim errWin As ErrorWindow = New ErrorWindow("ReadProductData", ex)
            errWin.ShowDialog()

        End Try

        Exit Sub
    End Sub

    ''' <summary>The GetProductDataTable method gets the table
    ''' with the product information from the product data set.
    ''' </summary>
    ''' <returns>The table with the product information if the data set
    ''' is valid. If there is no table, null is returned.</returns>
    Public Function GetProductDataTable() As System.Data.DataTable

        Dim dataTable As System.Data.DataTable = Nothing

        Try

            If (productDataSet IsNot Nothing) AndAlso (productDataSet.Tables.Count > 0) Then
                dataTable = New System.Data.DataTable

                ' There is only one table to return.
                dataTable = productDataSet.Tables(0)

            End If

        Catch ex As Exception

            Dim errWin As ErrorWindow = New ErrorWindow("GetProductDataTable", ex)
            errWin.ShowDialog()

        End Try


        Return dataTable

    End Function

    ''' <summary>The GetDecimalFieldValue method gets the decimal value
    ''' for the specified product and column in the data.</summary>
    ''' <param name="productId">The ID of the product to get the
    ''' decimal value for.</param>
    ''' <param name="columnName">The name of the column in the DataRow.</param>
    ''' <returns>The decimal value. If there is an error the return value is 0.
    ''' </returns>
    Private Function GetDecimalFieldValue(ByVal productId As Integer, ByVal columnName As String) As Decimal

        Dim price As Decimal = 0

        Dim productRow As DataRow

        productRow = findProduct(productId)

        If productRow IsNot Nothing Then

            price = Convert.ToDecimal(productRow(columnName), System.Globalization.CultureInfo.CurrentCulture)
        End If

        Return price
    End Function

    ''' <summary>The FindProduct method searches the data
    ''' for the specified product.</summary>
    ''' <param name="productId">The ID of the product.</param>
    ''' <returns>DataRow object if found, null if productId not found.</returns>
    Private Function findProduct(ByVal productId As Integer) As DataRow

        Dim dataRow As DataRow = Nothing

        Try

            ' Only handle a valid product ID.
            If (productId <> invalidProductId) AndAlso (GetProductDataTable() IsNot Nothing) Then

                Dim productRows As System.Data.DataRow()
                Dim dataFilter As String

                ' Get the row in the product data table that contains the
                ' product with the specified product id.
                dataFilter = (productIdColumnName & " = ") + productId.ToString(System.Globalization.CultureInfo.CurrentCulture)
                productRows = GetProductDataTable().[Select](dataFilter)

                ' Get the first matching data row.
                If (productRows IsNot Nothing) AndAlso (productRows.GetUpperBound(0) >= 0) Then

                    dataRow = productRows(0)
                End If
            End If
        Catch [error] As RowNotInTableException


            ' Consume this exception.
            Debug.WriteLine(([error].Message & " productId = ") + productId.ToString(System.Globalization.CultureInfo.CurrentCulture))
        End Try

        Return dataRow
    End Function

    ''' <summary>The IDisposable::Dispose method cleans up any resources being used.
    ''' </summary>
    Public Sub Dispose() Implements IDisposable.Dispose

        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ''' <summary>Internal Dispose method actually cleans up any resources being used and is 
    ''' called by public Dispose method. This method should only be called once. 
    ''' </summary>
    ''' <param name="disposing">This boolean indicates if this method is being called
    ''' by the public Dispose method or the destructor.</param> 
    Private Sub Dispose(ByVal disposing As Boolean)
        If Not disposed Then
            If disposing Then
                productDataSet.Dispose()
            End If
        End If
        disposed = True
    End Sub

    ''' <summary>
    ''' Destructor only is called if Dispose method is not called
    ''' </summary>
    Protected Overrides Sub Finalize()
        Try
            Dispose(False)
        Finally
            MyBase.Finalize()
        End Try
    End Sub


End Class


